/**
 * A Request useing App network request design {@link http://www.wanlshop.com}
 * @author 深圳前海万联科技有限公司 <wanlshop@i36k.com> < 本程序仅用作FastAdmin付费插件（多用户分销商城）测试使用，未经版权所有权人书面许可，不能自行用于商业用途！>
 * @2019年9月10日12:52:20
 * @version 1.0.1
 *
 **/
'use strict';
class Request {
  /**
   * @description 网络请求的默认配置
   * @property {Object} config - 默认参数配置
   * @property {string} config.baseUrl - 接口基地址
   * @property {string} config.business - 接口响应的业务数据对象字段名，默认为data
   */
  config = {
    /*返回默认为res.data*/
    baseUrl: '',
    //method: 'GET',
    //contentType: 'json',
    business: 'data',
    //dataType: 'json',
    //encoding: 'UTF-8',
    // skipInterceptorResponse: false,
    // slashAbsoluteUrl: true,
    // debug: false,
    // loadingTip: undefined,
    // loadingDuration: 500,
    // responseType: 'text'
  };

  static posUrl(url) {
    /* 判断url是否为绝对路径 */
    return /(http|https):\/\/([\w.]+\/?)\S*/.test(url);
  }

  static getUrl(config) {
    let url = config.url || '';
    let abs = Request.posUrl(url);
    if (!abs) {
      let f = config.slashAbsoluteUrl;
      if (f) {
        abs = /^\/([\w.]+\/?)\S*/.test(url);
      }
    }
    return abs ? url : config.baseUrl + url;
  }
  

  static getContentType(config) {
    var type = config.contentType || 'json';
    var charset = config.encoding || 'UTF-8';
    if (type === 'json') {
      return 'application/json;charset=' + charset;
    } else if (type === 'form') {
      return 'application/x-www-form-urlencoded;charset=' + charset;
    } else if (type === 'file') {
      return 'multipart/form-data;charset=' + charset;
    } else if (type === 'text') {
      return 'text/plain;charset=' + charset;
    } else if (type === 'html') {
      return 'text/html;charset=' + charset;
    } else {
      throw new Error('unsupported content type : ' + type);
    }
  }  
  
  /**
   * @property {Object} interceptor 拦截器
   *
   */
  interceptor = {
    /**
     * @description define the interceptor before request
     * @param {function}
     */
    request: undefined,
    response: undefined,
    fail: undefined,
  };

  /**
   * @description set default request options
   * @param {Object} config - the default options
   * @param {string} config.baseUrl baseUrl - the base url
   * @param {boolean} config.debug debug - enable debug to log
   */
  setConfig(config) {
    this.config = Object.assign(this.config, config);
  }

  request(options = {}) {
    const that = this;
    if (options.data === undefined) {
      options.data = {};
    }
    if (options.header === undefined) {
      options.header = {};
    }

    let _options = Object.assign({}, this.config, options);
    _options = Object.assign(options, _options);
    _options.url = Request.getUrl(_options);
    if (!_options.header['Content-Type']) {
      _options.header['Content-Type'] = Request.getContentType(_options);
    }
    let _config = _options;
    if (that.interceptor.request && typeof that.interceptor.request === 'function') {
      _config = that.interceptor.request(_options);
    }
    let task = undefined;
    let promise = new Promise((resolve, reject) => {
      let extras = {};
      that._prepare(that, _config, extras);
      // let cancel = (_config) => {
      //     that._fail(that, _config, {
      //         errMsg: 'request:canceled',
      //         statusCode: -1
      //     }, resolve, reject)
      //     next = false
      // }

      if (_config.contentType === 'file') {
        task = uni.uploadFile({
          ..._config,
          success: res => {
            that._success(that, _config, res, resolve, reject);
          },
          fail: res => {
            that._fail(that, _config, res, resolve, reject);
          },
          complete: res => {
            that._complete(that, _config, res, extras);
          },
        });
        if (_config.progress && typeof _config.progress === 'function') {
          task.onProgressUpdate(_res => {
            _config.progress(_res, task);
          });
        }
      } else {
        task = uni.request({
          ..._config,
          timeout: 6000,
          success: res => {
            that._success(that, _config, res, resolve, reject);
          },
          fail: res => {
            that._fail(that, _config, res, resolve, reject);
          },
          complete: res => {
            that._complete(that, _config, res, extras);
          },
        });
      }
    });
    if (_config.success || _config.fail || _config.complete) {
      return task;
    }
    return promise;
  }

  /**
 	 * @method
 	 * @description execute a get request
 	 * @param {Object} options - 参数选项
 	 * @param {string} options.url - 请求地址
 	 * @param {string} [options.method=GET] - 请求方法 GET|POST
 	 * @param {string} [options.contentType=json] - 请求类型，为json(默认)，form
 	 * @param {Object} [options.data] - 请求参数
 	 * @param {string} [options.encoding] - 请求编码，默认为utf-8
 	 * @param {string} [options.dataType] - 如果设为 json（默认），会尝试对返回的数据做一次 JSON.parse
 	 * @param {string} [options.business] - 接口响应的业务数据对象字段名，默认为data，如果返回整个业务对象，则需要设置为undefined
 	 * @param {string} [options.skipInterceptorResponse] - 是否跳过响应过滤器，如需跳过，请置true
 	 * @param {string} [options.slashAbsoluteUrl] - 是否视以/开头的url为绝对地址，默认为false，此设置仅当初步判断url为非绝对地址时有效
 	 * @param {string} [options.loadingTip] - 是否在请求前显示文字为参数值的loading提示，如果是，会在请求结束后自动关闭loading提示
 	 * @param {string} [options.loadingDuration] - 设置loadingTip时的最小loading显示时间
 	 *
 	 * @return {Promise} promise
 	 * @example
 	 * $request.get({
 	     url: 'foo/bar',
 	     data: {
 	         param1: value1
 	     }
 	 })
 	 * @see {@link https://uniapp.dcloud.io/api/request/request}
 	 */
  get(options = {}) {
    options.method = 'GET';
    return this.request(options);
  }

  /**
 	     * @method
 	     * @description execute a post request
 	     * @param {Object} options - 参数选项
 	     * @param {string} options.url - 请求地址
 	     * @param {string} [options.method=POST] - 请求方法 GET|POST
 	     * @param {string} [options.contentType=json] - 请求类型，为json(默认)，form
 	     * @param {Object} [options.data] - 请求参数
 	     * @param {string} [options.encoding] - 请求编码，默认为utf-8
 	     * @param {string} [options.dataType] - 如果设为 json（默认），会尝试对返回的数据做一次 JSON.parse
 	     * @param {string} [options.business] - 接口响应的业务数据对象字段名，默认为data，如果返回整个业务对象，则需要设置为undefined
 	     * @param {string} [options.skipInterceptorResponse] - 是否跳过响应过滤器，如需跳过，请置true
 	     * @param {string} [options.slashAbsoluteUrl] - 是否视以/开头的url为绝对地址，默认为false，此设置仅当初步判断url为非绝对地址时有效
 	     * @param {string} [options.loadingTip] - 是否在请求前显示文字为参数值的loading提示，如果是，会在请求结束后自动关闭loading提示
 	     * @param {string} [options.loadingDuration] - 设置loadingTip时的最小loading显示时间
 	     *
 	     * @return {Promise} promise
 	     * @example
 	     * $request.post({
 	        url: 'foo/bar',
 	        data: {
 	            param1: value1
 	        }
 	    })
 	    * @see {@link https://uniapp.dcloud.io/api/request/request}
 	    */
  post(options = {}) {
    options.method = 'POST';
    return this.request(options);
  }
  /**
 	 * @method
 	 * @description execute a get request
 	 * @param {Object} options - 参数选项
 	 * @param {string} options.url - 请求地址
 	 * @param {string} [options.method=GET] - 请求方法 GET|POST
 	 * @param {string} [options.contentType=json] - 请求类型，为json(默认)，form
 	 * @param {Object} [options.data] - 请求参数
 	 * @param {string} [options.encoding] - 请求编码，默认为utf-8
 	 * @param {string} [options.dataType] - 如果设为 json（默认），会尝试对返回的数据做一次 JSON.parse
 	 * @param {string} [options.business] - 接口响应的业务数据对象字段名，默认为data，如果返回整个业务对象，则需要设置为undefined
 	 * @param {string} [options.skipInterceptorResponse] - 是否跳过响应过滤器，如需跳过，请置true
 	 * @param {string} [options.slashAbsoluteUrl] - 是否视以/开头的url为绝对地址，默认为false，此设置仅当初步判断url为非绝对地址时有效
 	 * @param {string} [options.loadingTip] - 是否在请求前显示文字为参数值的loading提示，如果是，会在请求结束后自动关闭loading提示
 	 * @param {string} [options.loadingDuration] - 设置loadingTip时的最小loading显示时间
 	 *
 	 * @return {Promise} promise
 	 * @example
 	 * $request.upload({
 	    url: 'foo/bar',
 	    filePath: res.tempFilePaths[0];
 	    data: {
 	        param1: value1
 	    }
 	})
 	* @see {@link https://uniapp.dcloud.io/api/request/network-file}
 	*/
  upload(options = {}) {
    options.method = 'POST';
    options.contentType = 'file';
    return this.request(options);
  }

  _success = function (that, _config, res, resolve, reject) {
    if (res.statusCode >= 200 && res.statusCode <= 401) {
      // http ok
      var result = res.data; // 全局的拦截器
      var parseFileJson =
        _config.contentType === 'file' &&
        typeof result === 'string' &&
        (_config.dataType === undefined || _config.dataType === 'json');
      if (parseFileJson) {
        result = JSON.parse(res.data);
      }
      var skip = _config.skipInterceptorResponse;
      if (that.interceptor.response && typeof that.interceptor.response === 'function' && !skip) {
        // TODO 对于某些特殊接口，比如访问其它系统，全局拦截器可能不适合
        // 这种情况下，要根据_config在全局拦截器中将其它系统的返回适配为本系统的业务对象
        result = that.interceptor.response(result, _config);
      }
      if (skip || result.success) {
        // 接口调用业务成功
        var _data = _config.business ? result[_config.business] : result;
        if (_config.debug) {
          console.log('response success: ', _data);
        }
        _config.success ? _config.success(_data) : resolve(_data);
        return;
      }
    }
    that._fail(that, _config, res, resolve, reject);
  };

  _fail = function (that, _config, res, resolve, reject) {
    if (_config.debug) {
      console.error('response failure: ', res);
    }
    if (res.errMsg === 'request:fail abort') {
      return;
    }
    var result = res;
    if (that.interceptor.fail && typeof that.interceptor.fail === 'function') {
      result = that.interceptor.fail(res, _config);
    }
    _config.fail ? _config.fail(result) : reject(result);
  };

  _prepare = function (that, _config, obj = {}) {
    obj.startTime = Date.now();
    if (_config.loadingTip) {
      // 开启Loading -----------------------------------
      uni.showLoading({
        title: _config.loadingTip,
      });
    }
    if (_config.contentType === 'file') {
      if (_config.formData === undefined || _config.formData === null) {
        _config.formData = _config.data;
        delete _config.data;
      }
      delete _config.header['Content-Type'];
      delete _config.header['Referer'];
      _config.method = 'POST';
    }
    if (_config.debug) {
      console.log('request: ', _config, _config.data);
    }
  };

  _complete = function (that, _config, res, obj = {}) {
    obj.endTime = Date.now();
    if (_config.debug) {
      console.log('请求用时 ' + (obj.endTime - obj.startTime) + ' 毫秒');
    }
    if (_config.loadingTip) {
      let diff = obj.endTime - obj.startTime;
      let duration = _config.loadingDuration || 500;
      if (diff < duration) {
        diff = duration - diff;
      } else {
        diff = 0;
      }
      // 关闭Loading -----------------------------------
      setTimeout(function () {
        uni.hideLoading();
      }, diff);
    }
  };
}
/**
 *
 */
var request = new Request();
/**
 * @module {Request} request
 */
export default request;
